package folder.main.exp1.algorithms;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

import org.apache.commons.math3.stat.descriptive.SummaryStatistics;

import folder.model.network.Network;
import folder.model.network.NetworkPaths;
import folder.model.node.Node;

public class Algorithm1
{
	private Network networkPath;
	private int numberNodesSpecial;
	
	protected Comparator< Node > comparator;
	
	private double delayMeanSpecial;
	public double getDelayMeanSpecial(){ return this.delayMeanSpecial; }
	
	private double delayMeanNormal;
	public double getDelayMeanNormal(){ return this.delayMeanNormal; }
	
	private double differenceDelays;
	public double getDifferenceDelays(){ return this.differenceDelays; }
	
	private boolean checkDelays = false;
	public boolean getCheckDelays(){ return checkDelays; }
	
	public Algorithm1( Network networkPath, int numberNodesSpecial )
	{
		this.networkPath = networkPath;
		this.numberNodesSpecial = numberNodesSpecial;
	}
	
	protected void runAlgorithm()
	{
		networkPath.setPathsNetwork();
		
		List< Node > nodesSorted = this.getNodesSorted();
		List< Node > nodesSpecial = this.getNodesSpecial( nodesSorted );
		List< Node > nodesNormal = this.getNodesNormal( nodesSorted );
		
		this.setDelayMeanSpecial( nodesSpecial );
		
		this.disconnectNodes( nodesSpecial );
		networkPath.setPathsNodes( nodesNormal );
		this.connectNodes( nodesSpecial );
		
		this.setDelayMeanNormal( nodesNormal );
		this.setDifferenceDelays();
		this.setCheckDelays();
	}
	
	private List< Node > getNodesSorted()
	{
		List< Node > nodesSorted = new ArrayList< Node >();
		
		for( Node node: networkPath.getNodes() )
			nodesSorted.add( node );
		
		Collections.sort( nodesSorted, comparator );
		
		return nodesSorted;
	}
	
	private List< Node > getNodesSpecial( List< Node > nodesSorted )
	{
		List< Node > nodesSpecial = new ArrayList< Node >();
		
		for( int i = 0; i < networkPath.getNodes().size(); i++ )
		{
			Node nodeSorted = nodesSorted.get( i );
			
			if( i < numberNodesSpecial )
			{
				nodeSorted.setSelected( true );
				nodesSpecial.add( nodeSorted );
			}
			else
				nodeSorted.setSelected( false );
		}
		
		return nodesSpecial;
	}
	
	private List< Node > getNodesNormal( List< Node > nodesSorted )
	{
		List< Node > nodesNormal = new ArrayList< Node >();
		
		for( Node nodeSorted: nodesSorted )
			if( !nodeSorted.isSelected() )
				nodesNormal.add( nodeSorted );
		
		return nodesNormal;
	}
	
	private void setDelayMeanSpecial( List< Node > nodesToSet )
    {
    	SummaryStatistics sumTotal = new SummaryStatistics();
    	
    	for( Node node: nodesToSet )
    	{
    		SummaryStatistics sum = new SummaryStatistics();
    		
    		Iterator< Entry< Node, Double > > iterator = node.getIteratorDelayToNodeTarget();
    		
    		while( iterator.hasNext() )
    		{
    			Entry< Node, Double > entry = iterator.next();
    			Node nodeTarget = entry.getKey();
    			double delayToNodeTarget = entry.getValue();
    			
    			if( !nodeTarget.isSelected() )
    				sum.addValue( delayToNodeTarget );
    		}
    		
    		sumTotal.addValue( sum.getMean() );
    	}
    	
    	delayMeanSpecial = sumTotal.getMean();
    }

	private void disconnectNodes( List< Node > nodesToDisconnect )
	{
		for( Node node: nodesToDisconnect )
			node.disconnectLinks();
	}
	
	private void connectNodes( List< Node > nodesToConnect )
	{
		for( Node node: nodesToConnect )
			node.connectLinks();
	}
	
	private void setDelayMeanNormal( List< Node > nodesToSet )
    {
    	SummaryStatistics sumTotal = new SummaryStatistics();
    	
    	for( Node node: nodesToSet )
    	{
    		SummaryStatistics sum = new SummaryStatistics();
    		
    		Iterator< Entry< Node, Double > > iterator = node.getIteratorDelayToNodeTarget();
    		
    		while( iterator.hasNext() )
    		{
    			Entry< Node, Double > entry = iterator.next();
    			double delayToNodeTarget = entry.getValue();
    			
    			if( delayToNodeTarget < Double.POSITIVE_INFINITY )
    				sum.addValue( delayToNodeTarget );
    		}
    		
    		sumTotal.addValue( sum.getMean() );
    	}
    	
    	this.delayMeanNormal = sumTotal.getMean();
    }
	
	private void setDifferenceDelays()
	{
		differenceDelays = Math.abs( delayMeanSpecial - delayMeanNormal );
	}
	
	private void setCheckDelays()
	{
		if( delayMeanSpecial < Double.POSITIVE_INFINITY && 
			delayMeanNormal  < Double.POSITIVE_INFINITY && 
			differenceDelays < Double.POSITIVE_INFINITY )
				checkDelays = true;
			else
				checkDelays = false;
	}
	
}
